"""
# This a Sudok_Game Generation program
# based on python 3.8
# need python itertools,sys module
#
# Parameters :
#     parameter1-this is the first parameter
#         -c: which means to generate the some sudok_maps.
#         -s: which means to solve the puzzle file.
#     parameter2-this is the second parameter
#         If the parameter1 is "-c":
#           the parameter2 should be the number of the Generation sudok_maps
#           Tips:the number should be the INT type and more than 1 while less than 1000000
#         If the parameter1 is "-s"
#           the parameter2 should be the absolute_path of the puzzle file
#
# Returns :
#     If the parameter1 is "-c":
#         Generate some sudok_maps in the file "Sudok.txt"
#     If the parameter1 is "-s":
#         Generate one of the possible solutions for each puzzle in the puzzlefile
#         Write them to the file "sudok.txt"
"""

import itertools
import sys
import random
import numpy as np
import sudok_solve


def gen_line_one():  # 生成随机的第一行数据
    """
    This is a subfunction which generate the first line of the sudok_map
    :parameter
        NULL
    :return:
        return a list([40320,8]) which include all the permutations
    """
    conter = [1, 2, 3, 4, 5, 6, 8, 9]
    return list(itertools.permutations(conter))


# def Gen_Line_One():  # 生成随机的第一行数据
#     nums = [1, 2, 3, 4, 5, 6, 8, 9]
#
#     def Backtrack(first=0):
#         if (first == n):
#             res.append(nums[:])
#         for i in range(first, n):
#             nums[first], nums[i] = nums[i], nums[first]
#             Backtrack(first + 1)
#             nums[first], nums[i] = nums[i], nums[first]
#
#     n = len(nums)
#     res = []
#     Backtrack()
#     return res


def gen_line(bias, line_one):  # 根据第一行数据生成其余每行数据
    """
    This function generate other lines in the sudok_map with the certain line_one
    :parameter
        bias: for each line ,it has a different bias based on the line_one
        line_one:different line_one could make different sudok_maps.
    :returns
        return the others line with a BIAS as bias.
    """
    line = line_one.copy()
    index = 0
    while index < bias:
        line.insert(0, line[8])
        line.pop(9)
        index += 1
    return line


def gen_all_map(line_one):  # 按照次序生成盘数据
    """
    With the help of the Function gen_line
    This function generate the sudok_map all line by line.
    :parameter
        line_one: different line_one could make different sudok_maps.
    :returns
        return the whole sudok_map.
    """
    bias = [3, 6, 1, 4, 7, 2, 5, 8]
    sudok_map_1 = []
    sudok_map_1.append(line_one)
    for item_bias in bias:
        sudok_map_1.append(gen_line(item_bias, line_one))
    # print(Sudok_Map)
    return sudok_map_1


def check_sudok_map(sudok_map_check):  # 生成终局的正确性检查
    """
    After the gen_all_map Function.
    This function is to check the map whether correct or not.
    Based on the Zone_Index algorithm.
    :param
        sudok_map_check:the sudok_map which gen_all_map generated.
    :return:
    """
    temp_line = [1, 2, 3, 4, 5, 6, 7, 8, 9]
    temp_map = []
    for i in range(9):
        temp_map.append(temp_line.copy())
    for row in range(9):
        for column in range(9):
            blockindex = (row // 3) * 3 + column // 3
            temp_map[blockindex][sudok_map_check[row][column] - 1] = -1
    for item_check in temp_map:
        temp_sum = 0
        for i in range(9):
            temp_sum += item_check[i]
        if temp_sum != -9:
            return False
    return True


def bloom(line2_3, line4_6, line7_9, sudok_map_ori):
    """
    After the Check of check_sudok_map and suggest it's correct.
    This function is to swap some particular lines in the map in order to make more standard maps.
    :parameter
        line2_3: the method that swap the line2 and line3.
        line4_6: the method that swap the line4,line5 and line6.
        line7_9: the method that swap the line7,line8 and line9.
        sudok_map_ori: the standard sudok_map .
    :return:
        the standard sudok_map after the certain swaps.
    """
    sudok_map_2 = sudok_map_ori.copy()
    if line2_3 == 1:
        temp = sudok_map_2[1]
        sudok_map_2[1] = sudok_map_2[2]
        sudok_map_2[2] = temp

    if line4_6 == 1:  # 435
        temp = sudok_map_2[3]
        sudok_map_2[3] = sudok_map_2[4]
        sudok_map_2[4] = temp
    elif line4_6 == 2:  # 354
        temp = sudok_map_2[4]
        sudok_map_2[4] = sudok_map_2[5]
        sudok_map_2[5] = temp
    elif line4_6 == 3:  # 543
        temp = sudok_map_2[3]
        sudok_map_2[3] = sudok_map_2[5]
        sudok_map_2[5] = temp
    elif line4_6 == 4:  # 453
        temp = sudok_map_2[3]
        sudok_map_2[3] = sudok_map_2[4]
        sudok_map_2[4] = temp
        temp = sudok_map_2[4]
        sudok_map_2[4] = sudok_map_2[5]
        sudok_map_2[5] = temp
    elif line4_6 == 5:  # 534
        temp = sudok_map_2[3]
        sudok_map_2[3] = sudok_map_2[5]
        sudok_map_2[5] = temp
        temp = sudok_map_2[4]
        sudok_map_2[4] = sudok_map_2[5]
        sudok_map_2[5] = temp

    if line7_9 == 1:  # 768
        temp = sudok_map_2[6]
        sudok_map_2[6] = sudok_map_2[7]
        sudok_map_2[7] = temp
    elif line7_9 == 2:  # 687
        temp = sudok_map_2[7]
        sudok_map_2[7] = sudok_map_2[8]
        sudok_map_2[8] = temp
    elif line7_9 == 3:  # 876
        temp = sudok_map_2[6]
        sudok_map_2[6] = sudok_map_2[8]
        sudok_map_2[8] = temp
    elif line7_9 == 4:  # 786
        temp = sudok_map_2[6]
        sudok_map_2[6] = sudok_map_2[7]
        sudok_map_2[7] = temp
        temp = sudok_map_2[7]
        sudok_map_2[7] = sudok_map_2[8]
        sudok_map_2[8] = temp
    elif line7_9 == 5:  # 867
        temp = sudok_map_2[6]
        sudok_map_2[6] = sudok_map_2[8]
        sudok_map_2[8] = temp
        temp = sudok_map_2[7]
        sudok_map_2[7] = sudok_map_2[8]
        sudok_map_2[8] = temp

    return sudok_map_2


def map_one_to_72(sudok_map_origion):
    """
    With the help of the Function bloom.
    This function generate some certain parameters to make a new standard sudok_map.
    :parameter
        sudok_map_origion: the standard sudok_map .
    :return:
        a list which contains 72 standrad sudok_maps.
    """
    map_list_72 = []
    for line2_3 in range(2):
        for line4_6 in range(6):
            for line7_9 in range(6):
                map_list_72.append(bloom(line2_3, line4_6, line7_9, sudok_map_origion))
    return map_list_72


def map_to_txt(sudok_map_set, cont, flag):
    """
    This function translate the sudok_map list into a file("Sudok.txt")
    :parameter
        sudok_map_set: the standard sudok_map set.
        cont: how many maps need to be writen into the file
    :return:
        the new file Sudok.txt is generated.
    """
    if flag == 1:
        file_save_path = 'Sudoku.txt'
    elif flag == 2:
        file_save_path = 'Sudok_puzzle.txt'
    else:
        file_save_path = 'Sudok_puzzle_Solved.txt'
    file = open(file_save_path, 'w+')
    cnt = 0
    for item in sudok_map_set:
        if cnt < cont:
            cnt += 1
            for line in item:
                con = 0
                line_str = ''
                for num in line:
                    con += 1
                    if con == 9:
                        line_str += str(num)
                    else:
                        line_str += str(num)
                        line_str += ' '
                file.write(line_str)
                file.write('\n')
            print("已写入-->", cnt)
            file.write('\n')
        else:
            file.close()
            break


def gen_sudok_puzzle(sudok_map_set, cont):
    '''
    This function is to generate the sudok_puzzle with the sudok_map_set
    :parameter
        sudok_map_set: the standard sudok_map set.
        cont: how many maps need to be writen into the file.
    :return:
        the sudok_puzzle set which is list data struct
    :return:
    '''
    blockindex_1 = [0, 1, 2]
    blockindex_2 = [3, 4, 5]
    blockindex_3 = [6, 7, 8]
    block_1 = [blockindex_1, blockindex_1]
    block_2 = [blockindex_1, blockindex_2]
    block_3 = [blockindex_1, blockindex_3]
    block_4 = [blockindex_2, blockindex_1]
    block_5 = [blockindex_2, blockindex_2]
    block_6 = [blockindex_2, blockindex_3]
    block_7 = [blockindex_3, blockindex_1]
    block_8 = [blockindex_3, blockindex_2]
    block_9 = [blockindex_3, blockindex_3]
    block_set = [block_1, block_2, block_3, block_4, block_5, block_6, block_7, block_8, block_9]
    parten_1 = [[0, 0], [0, 3], [2, 0], [2, 2]]
    parten_2 = [[0, 1], [1, 0], [1, 2], [2, 1]]
    parten_3 = [[0, 0], [1, 1], [2, 2], [0, 2]]
    parten_4 = [[0, 2], [1, 1], [2, 0], [2, 2]]
    parten_5 = [[0, 0], [1, 1], [2, 2], [2, 0]]
    parten_6 = [[0, 0], [0, 2], [1, 1], [2, 0]]
    parten_7 = [[0, 0], [1, 0], [2, 0], [1, 1]]
    parten_8 = [[1, 1], [0, 2], [1, 2], [2, 2]]
    parten_9 = [[1, 0], [1, 1], [1, 2], [2, 1]]
    parten_10 = [[0, 1], [1, 0], [1, 1], [1, 2]]
    parten = [parten_1, parten_2, parten_3, parten_4, parten_5, parten_6, parten_7, parten_8, parten_9, parten_10]
    sudok_map_3 = sudok_map_set.copy()
    sudok_map_3 = np.array(sudok_map_3)
    sudok_puzzle = []
    for index in range(cont):
        sudok_item = sudok_map_3[index]
        for block in block_set:
            parten_index = random.randint(1, 9)
            sudok_item[block[0][parten[parten_index][0][0]]][block[1][parten[parten_index][0][1]]] = 0
            sudok_item[block[0][parten[parten_index][1][0]]][block[1][parten[parten_index][1][1]]] = 0
            sudok_item[block[0][parten[parten_index][2][0]]][block[1][parten[parten_index][2][1]]] = 0
            sudok_item[block[0][parten[parten_index][3][0]]][block[1][parten[parten_index][3][1]]] = 0
        print(sudok_item)
        sudok_puzzle.append(sudok_item)
    # print(sudok_puzzle)
    return sudok_puzzle


if __name__ == '__main__':
    if sys.argv[1] == "-c":
        try:
            need_map_cont = int(sys.argv[2])
            batch = (need_map_cont // 72) + 1
        except ValueError:
            print("请输入正确参数")
        else:
            all_sudok_map = []
            line_one_set = gen_line_one()
            for num_batch in range(batch):
                Line_One = list(line_one_set[num_batch])
                Line_One.insert(0, 7)
                sudok_map = gen_all_map(Line_One)
                if check_sudok_map(sudok_map):
                    for item_in_72 in map_one_to_72(sudok_map):
                        all_sudok_map.append(item_in_72)
            # print(all_sudok_map)
            map_to_txt(all_sudok_map, need_map_cont, 1)
            map_to_txt(gen_sudok_puzzle(all_sudok_map, need_map_cont), need_map_cont, 2)
    elif sys.argv[1] == "-s":
        sudok_puzzle_solved = []
        sudok_puzzle = np.loadtxt('Sudok_puzzle.txt', dtype=bytes).astype(int)
        sudok_puzzle = sudok_puzzle.reshape(-1, 81)
        solve_cont = 0
        for item in sudok_puzzle:
            solve_cont += 1
            print("已完成第", solve_cont, "解算")
            sudoku = []
            solved = []
            for i in item:
                sudoku.append(i - 1)
            solver = sudok_solve.SudokuSolver(sudoku)
            for i in range(len(solver.answer)):
                solved.append(solver.answer[i] + 1)
            solved = np.array(solved).reshape(9, 9)
            sudok_puzzle_solved.append(solved)
        # print(sudok_puzzle_solved)
        map_to_txt(sudok_puzzle_solved, len(sudok_puzzle_solved), 3)
